using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using UnityEngine;
using UnityEngine.SceneManagement;
using Object = UnityEngine.Object;

namespace JH
{
    /// <summary>
    /// 应用场景：在所有Object上，寻找所有标记了某个特性的成员
    /// </summary>
    public static class GetObjectMemberInfosByAttribute
    {
        /// <summary>
        ///  缓存字典，用于存储已经查找过的Object及其成员
        /// key 为元组，包含特性类型和是否包含未激活物体的布尔值
        ///  value 为字典，包含Object和成员信息列表
        /// </summary>
        private static Dictionary<(Type, bool), Dictionary<Object, HashSet<MemberInfo>>> _cache;

        /// <summary>
        ///  缓存字典，用于存储已经查找过的Object及其成员
        /// </summary>
        private static Dictionary<(Type, bool), Dictionary<Object, HashSet<MemberInfo>>> Cache
        {
            get
            {
                if (_cache != null) return _cache;
                _cache = new Dictionary<(Type, bool), Dictionary<Object, HashSet<MemberInfo>>>();
                //监听场景切换事件，清空缓存
                SceneManager.sceneUnloaded += (scene) => _cache.Clear();
                return _cache;
            }
        }

        /// <summary>
        ///  缓存：key :类类型，特性类型
        ///  value: 成员类型
        /// </summary>
        private static readonly Dictionary<(Type classType, Type attributeType), HashSet<MemberInfo>> TypeMemberCache =
            new();

        /// <summary>
        /// 扩展方法，接收一个 Type 类型的参数，返回一个包含 Object 和 FieldInfo 列表的字典
        /// </summary>
        /// <param name="attributeType">特性类型</param>
        /// <param name="isIncludeInactive">是否查询未激活的物体</param>
        /// <param name="forceRefresh">是否强制刷新，重新查找</param>
        /// <param name="withAssets">是否包含资产</param>
        /// <returns>返回寻找的结果</returns>
        public static Dictionary<Object, HashSet<MemberInfo>> GetObjectMemberInfosByAttributeEm<T>(
            this Type attributeType, bool isIncludeInactive = false, bool forceRefresh = false, bool withAssets = false)
            where T : Object
        {
            // 检查传入的类型是否是 Attribute 或其派生类的类型，如果不是，抛出异常
            if (!typeof(Attribute).IsAssignableFrom(attributeType))
                throw new ArgumentException("传入的类型必须是 Attribute 或其派生类的类型。", nameof(attributeType));

            //如果缓存中已经存在，并且不强制刷新，则直接返回
            if (!forceRefresh && Cache.TryGetValue((attributeType, isIncludeInactive),
                    out var retDict))
            {
                return CleanMissingComponent(retDict);
            }

            // 创建一个空字典，用于存储结果
            var typesWithAttributeDict = new Dictionary<Object, HashSet<MemberInfo>>();

            // 获取所有Object
            var allObject = withAssets
                ? Resources.FindObjectsOfTypeAll<T>()
                : Object.FindObjectsByType<T>(
                    isIncludeInactive ? FindObjectsInactive.Include : FindObjectsInactive.Exclude
                    , FindObjectsSortMode.None);

            // 遍历场景中的每个Object
            foreach (var obj in allObject)
            {
                // 获取该Object的所有标记了特定属性的成员
                var fieldsWithAttribute = GetMembersWithAttribute(obj.GetType(), attributeType);

                // 如果该组件有一个或多个标记了特定属性的成员
                if (fieldsWithAttribute.Count > 0)
                {
                    // 将该组件及其对应的成员添加到结果字典中
                    typesWithAttributeDict[obj] = fieldsWithAttribute;
                }
            }

            // 将结果字典添加到缓存中
            Cache[(attributeType, isIncludeInactive)] = typesWithAttributeDict;

            // 返回结果字典
            return CleanMissingComponent(typesWithAttributeDict);
        }

        /// <summary>
        ///  清理无效Object
        /// </summary>
        private static Dictionary<Object, HashSet<MemberInfo>> CleanMissingComponent(
            Dictionary<Object, HashSet<MemberInfo>> cachedResult)
        {
            return cachedResult.Where(kvp => kvp.Key != null && kvp.Value != null)
                .ToDictionary(kvp => kvp.Key, kvp => kvp.Value);
        }

        /// <summary>
        /// 获取指定类型和特性类型的成员信息
        /// </summary>
        private static HashSet<MemberInfo> GetMembersWithAttribute(Type type, Type attributeType)
        {
            var cacheKey = (type, attributeType);

            // 如果已缓存此类型的结果，直接返回
            if (TypeMemberCache.TryGetValue(cacheKey, out var cachedMembers))
                return cachedMembers;

            // 否则分析类型层次结构并缓存结果
            var result = new HashSet<MemberInfo>();
            var flags = CommonlyUseFlag.GetCommonlyUseFlagsEm();
            while (type != null && type != typeof(Object))
            {
                // 获取该组件的所有成员，包括公有成员、非公有成员和实例成员
                var members = type.GetMembers(flags);

                foreach (var memberInfo in members)
                {
                    // 如果成员标记了特定属性 将该成员添加到结果字典中
                    if (memberInfo.GetCustomAttributes(attributeType, false).Length > 0)
                        result.Add(memberInfo);
                }

                type = type.BaseType;
            }

            // 缓存结果
            TypeMemberCache[cacheKey] = result;
            return result;
        }
    }
}